//============================================================================
// Name        : SlidingWindow_Selective.cpp
// Author      : WincloudBear(???)
// Version     :
// Copyright   : Your copyright notice
// Description : in C++, Ansi-style
//============================================================================

#include <stdio.h>
#include <string.h>

#include "protocol.h"
#include "datalink.h"

//#define DEBUG

#define DATA_TIMER  2500
#define ACK_MS 700
#define MAX_SEQ 31
#define NR_BUFS ((MAX_SEQ+1)/2)

typedef struct {
	unsigned char data[PKT_LEN];
} packet;

typedef struct FRAME {
	unsigned char kind; /* FRAME_DATA */
	unsigned char ack;
	unsigned char seq;
	packet data;
	unsigned int padding;
} frame;

//static unsigned char frame_nr=0, nbuffered=0;
//static unsigned char frame_expected = 0;
static unsigned char phsicical_layer_ready = 0, no_nak = 1, oldest_frame = MAX_SEQ + 1;

static int between(int a, int b, int c) {
	return ((a <= b && b < c) || (c < a && a <= b) || (c < a && b < c));
}

static void put_frame(unsigned char *frame, int len) {
	*(unsigned int *) (frame + len) = crc32(frame, len);
	send_frame(frame, len + 4);
	phsicical_layer_ready = 0;
	stop_ack_timer();
}

static void mysend_frame(unsigned char fk, unsigned char frame_nr,
		unsigned char frame_expected, packet buffer[]) {
	frame s;
	int len = 3;
	s.kind = fk;
	s.seq = frame_nr;
	s.ack = (frame_expected + MAX_SEQ) % (MAX_SEQ + 1);

	switch (fk) {
	case FRAME_DATA:
		len += PKT_LEN;
		memcpy(s.data.data, &buffer[frame_nr % NR_BUFS].data, PKT_LEN);
		//			printf("send data %d\n",s.seq);
		dbg_frame("Send DATA %d %d, ID %d\n", s.seq, s.ack,
				*(short *) s.data.data);

		break;
	case FRAME_NAK:
		dbg_frame("Send NAK  %d\n", s.ack);
		no_nak = 0;
		break;
	case FRAME_ACK:
		dbg_frame("Send ACK  %d\n", s.ack);
		break;
	}

	put_frame((unsigned char *) &s, len);
	if (fk == FRAME_DATA)
		start_timer(frame_nr % NR_BUFS, DATA_TIMER);
	stop_ack_timer();
}

void inc(unsigned char *x) {
	(*x) = (*x + 1) % (MAX_SEQ + 1);

}

int main(int argc, char **argv) {
	int event, arg;
	unsigned char ack_expected = 0, next_frame_to_send = 0, too_far = NR_BUFS,
			frame_expected = 0, nbuffered = 0;

	frame f;
	int len = 0;
	packet out_buf[NR_BUFS], in_buf[NR_BUFS];
	unsigned char arrived[NR_BUFS];
	int i, loop = 1;

	for (i = 0; i < NR_BUFS; i++)
		arrived[i] = 0;
	i = 0;

	protocol_init(argc, argv);
	lprintf("Designed by WincloudBear, build: " __DATE__"  "__TIME__"\n");

	disable_network_layer();

	while (1) {
#ifdef DEBUG
		log_printf("\n\n\n-----------------------\n"
				"this is loop %7d\n",loop++);
#endif // DEBUG
		event = wait_for_event(&arg);
		//printf("phsicical_layer_ready:%d",phsicical_layer_ready);
		switch (event) {
		case NETWORK_LAYER_READY:
#ifdef DEBUG
			log_printf("\n\t\tNETWORK_LAYER_READY\n\n");
#endif

			nbuffered++;
			get_packet(out_buf[next_frame_to_send % NR_BUFS].data);
			mysend_frame(FRAME_DATA, next_frame_to_send, frame_expected,
					out_buf);
			inc(&next_frame_to_send);
			break;
		case PHYSICAL_LAYER_READY:
#ifdef DEBUG
			log_printf("\n\t\tPHYSICAL_LAYER_READY\n\n");
#endif
			phsicical_layer_ready = 1;
			//printf("phsicical_layer_ready:%d",phsicical_layer_ready);
			break;
		case FRAME_RECEIVED:
#ifdef DEBUG
			log_printf("\n\t\tFRAME_RECEIVED\n\n");
#endif
			len = recv_frame((unsigned char *) &f, sizeof f);
			if (len < 5 || crc32((unsigned char *) &f, len) != 0) {
				dbg_event("**** Receiver Error, Bad CRC Checksum,number:%d\n",
						f.seq);
				if (no_nak)
					mysend_frame(FRAME_NAK, 0, frame_expected, out_buf);
				break;
			}
			if (f.kind == FRAME_DATA) {
				dbg_frame("\tRecv DATA %d %d, ID %d\n", f.seq, f.ack,
						*(short *) f.data.data);
				if (f.seq != frame_expected && no_nak)
					mysend_frame(FRAME_NAK, 0, frame_expected, out_buf);
				else
					start_ack_timer(ACK_MS);
				if (between(frame_expected, f.seq, too_far) && (arrived[f.seq
						%NR_BUFS] == 0)) {
					arrived[f.seq % NR_BUFS] = 1;
					in_buf[f.seq % NR_BUFS] = f.data;
					while (arrived[frame_expected % NR_BUFS]) {
#ifdef DEBUG
						log_printf("\n\tframe_expected:%d,too_far:%d\n"
								"\tack_expected:%d,next_frame_to_send:%d\n"
								,frame_expected,too_far,ack_expected,next_frame_to_send);
						log_printf("frame_id:%d,  \n",*(short *)in_buf[frame_expected%NR_BUFS].data);
						if((*(short *)in_buf[frame_expected%NR_BUFS].data-i)%1000!=0) {
							log_printf("problem here\n%d\n\n"
									,frame_expected%NR_BUFS);

						}
						i++;
#endif

						put_packet(in_buf[frame_expected % NR_BUFS].data, len
								- 7);
						no_nak = 1;
						arrived[frame_expected % NR_BUFS] = 0;
						inc(&frame_expected);
						inc(&too_far);
						start_ack_timer(ACK_MS);
					}
				}
			}
			if (f.kind == FRAME_ACK) {
				dbg_frame("Recv ACK  %d\n", f.ack);
			}
			if (f.kind == FRAME_NAK) {
				dbg_frame("Recv NAK  %d\n", f.ack);
				if (between(ack_expected, (1 + f.ack) % (MAX_SEQ + 1),
						next_frame_to_send))
					mysend_frame(FRAME_DATA, (1 + f.ack) % (MAX_SEQ + 1),
							frame_expected, out_buf);
			}
			while (between(ack_expected, f.ack, next_frame_to_send)) {
				nbuffered--;
				stop_timer(ack_expected % NR_BUFS);
				inc(&ack_expected);
			}
			break;

		case DATA_TIMEOUT:

#ifdef DEBUG
			log_printf("\n\t\tDATA_TIMEOUT\n\n");
#endif
			if (!between(ack_expected, arg, next_frame_to_send))
				arg += NR_BUFS;
			dbg_event("---- DATA %d timeout\n", arg);
#ifdef DEBUG
			if (arg>=NR_BUFS)
			{
				log_printf("arg here");
			}
#endif // DEBUG
			mysend_frame(FRAME_DATA, arg, frame_expected, out_buf);
			//		mysend_frame(FRAME_DATA,(*(short *)out_buf[arg].data%10000)%(MAX_SEQ+1)
			//		,frame_expected,out_buf);

			break;

		case ACK_TIMEOUT:
#ifdef DEBUG
			log_printf("\n\t\tACK_TIMEOUT\n\n");
#endif
			dbg_event("---- ACK %d timeout\n", arg);
			mysend_frame(FRAME_ACK, 0, frame_expected, out_buf);
			break;

		}
		if (nbuffered < NR_BUFS && phsicical_layer_ready) {
#ifdef DEBUG
			log_printf("\n\t\tenable_network_layer\n\n");
#endif
			enable_network_layer();
		} else {
#ifdef DEBUG
			log_printf("\n\t\tdisable_network_layer\n\n");
#endif
			disable_network_layer();
		}

	}

}
